package controler;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;


import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import model.DocumentXML;
import model.NodeXML;
import model.Term;
import model.TermNode;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import services.NodeXMLService;
import services.WeightService;

public class IndexControler {
	
	private int indexTerm;
	private int indexDocument;
	
	private String filePath; // path des documents XML à parser
	private ArrayList<String> listeMotsVides;	// Initialisation des mots vides 
	private JDBCHandler handlerJDBC;	// JDBC
	
	private HashMap<String,Term> listTermToAdd; //Liste des termes que l'on a ajouté en base
	private HashMap<Integer, NodeXML> listNodeToAdd; //Liste des noeuds a ajouter
	private HashMap<Integer, Integer> listNumberOfNodeToTerm; //Liste des noeuds a ajouter
	private HashMap<String, TermNode> listTermNodeToAdd;
	private ArrayList<DocumentXML> listDocumentXMLToAdd;
	
	/** Constructor **/
	public IndexControler() {
		
		super();
		
		this.listeMotsVides = InitlisteMotsVides(); //Initalise la liste des mots vides
		
		this.handlerJDBC = new JDBCHandler(); // création jbdd
		
		//Initialise les listes
		this.listTermToAdd = new HashMap<String,Term>();
		this.listNodeToAdd = new HashMap<Integer,NodeXML>();
		this.listTermNodeToAdd = new HashMap<String, TermNode>();
		this.listDocumentXMLToAdd = new ArrayList<DocumentXML>();
		this.listNumberOfNodeToTerm = new HashMap<Integer, Integer>();
		
		//Cherche le plus grand id en base et l'affecte
		this.indexTerm = 0;
		this.indexDocument = 0;
		
	}
	
	/** Indexation des termes**/
	public void indexTerm(String filePath, String extention, ArrayList<String> tagList) {
		
		this.filePath = filePath;
	      try{
	    	  
	    	  handlerJDBC.clearDataBase(); //Nettoyage BDD à chaque indexation
	          DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // Création fabrique de document
	          DocumentBuilder builder = factory.newDocumentBuilder();// création d'un constructeur de documents
	          
	          File folder = new File(filePath);
	          File[] listOfFiles = folder.listFiles();
	          
	          for (File file : listOfFiles) {
	        	  
	        	  if (file.isFile() && file.getName().endsWith(extention)) {
	        		  
	        		  Document document = builder.parse(file); // Parsing du fichier
	        		  
	        		  // Ajout du document
	        		  DocumentXML currentDocument = new DocumentXML(indexDocument,file.getName(), filePath);
	        		  this.indexDocument++;
	        		  this.listDocumentXMLToAdd.add(currentDocument);
	        		  
	        		  // Récupération de l'élément racine
	        		  Element racine = document.getDocumentElement();
	        		  
	        		  // On récupère une liste de node contenant toutes les balises
	        		  for (String tag : tagList) {
	        			  
	        			  NodeList nodeList = racine.getElementsByTagName(tag);
	        			  
	        			  for(int i=0; i<nodeList.getLength(); i++){
	        				  
	        				  Element elementNode = (Element)nodeList.item(i);
	        				  String tagContent = elementNode.getTextContent();
	        				  int sizeNode =  NodeXMLService.getSizeNode(tagContent);
	        				  
	        				  String pathNode = getPathNode(elementNode);
	        				  // Stock du node dans la liste
	        				  NodeXML nodeXml = new NodeXML(this.listNodeToAdd.size(), pathNode, currentDocument.getId(),sizeNode);
	        				  this.listNodeToAdd.put(nodeXml.getIdNode(), nodeXml );
	        				  
	        				  // Séparateur de mots : creation tableau
	        				  String[] tabWords = tagContent.split("[\\s,;.'-:!?/&~]");
	        				  
	        				  // Parcour des mots du fichier
	        				  for(int j = 0; j<tabWords.length; j++){
	        					  
	        					  int cptWords = j+1;
	        					  
	        					  // ToLower
	        					  String word = tabWords[j].toLowerCase();
	        					  //Si ce n'est pas un mot considéré comme bruit
	        					  if (!listeMotsVides.contains(word) && isAlphaNumeric(word) && word.length() >= 2) {
	        						  
	        						  String wordLem = null;
	        						  // Lemmatisation
	        						  if (word.length() > 6) {
	        							  
	        							  wordLem = word.substring(0, 6);
	        						  } else {
	        							  wordLem = word;
	        						  }
        							  //On verifie si le terme n'est pas déjà en base sinon on l'ajoute
        							  Term term = null;
        							  if(!this.listTermToAdd.containsKey(wordLem)){
        								  
        								  term = new Term(this.indexTerm, wordLem);
        								  this.indexTerm ++;
        								  //On ajoute le mot ajouté à la liste referençant les mots en base
        								  this.listTermToAdd.put(wordLem, term);
        							  }else{
        								  term = this.listTermToAdd.get(wordLem);
    								  }
        							  
        							  String indTermNode = Integer.toString(nodeXml.getIdNode())+"-"+Integer.toString(term.getIdTerm())+"-"+file.getName();
        							  
        							  
        							  //Si le termNode est déjà présent
        							  TermNode termNode = null;
        							  if(listTermNodeToAdd.containsKey(indTermNode)){
        								  //On recupere le term node pour incrementer sa position et ajouter
        								  //la position du mot similaire
        								  //On recupere l'element à partir de son indice dans la liste
        								  
        								  termNode = this.listTermNodeToAdd.get(indTermNode);
        								  termNode.setPosition(termNode.getPosition()+"-"+cptWords);
        								  termNode.setFrequency(termNode.getFrequency() + 1);
        								  
        								  //Reference ou copier à nouveau dans le tableau ???
    								  }else{
    									  termNode = new TermNode(nodeXml.getIdNode(), term.getIdTerm(),  ""+cptWords, 0, 1);
    									  
    									  // On compte le nb de node pour ce terme
    									  int cpt = 1;
    									  if (this.listNumberOfNodeToTerm.containsKey(term.getIdTerm())) {
    										  cpt = cpt + listNumberOfNodeToTerm.get(term.getIdTerm());
    									  }
    									  listNumberOfNodeToTerm.put(term.getIdTerm(), cpt);
    								  }
        							  

									  this.listTermNodeToAdd.put(indTermNode,termNode);
									  
//	        							  System.out.println(	"Document : "+this.currentDocument.getName()+
//																" - Noeud : "+this.nodexml.getPath()+
//																" - idNode : "+this.nodexml.getIdNode()+
//																" - idMot : "+this.term.getIdTerm()+
//																" - Mot : "+this.term.getValue()+
//																" - Position : "+this.termNode.getPosition()+
//																" - Poids : "+this.termNode.getWeight() );
        							  
	        						  }
	        					  }
	        				  }
	        			  }
			          	System.out.println(file.getName()+" parsed...");
	        		  }
	        	  }
	          
	          //Calculate weight of all term node
	          double executionTime = System.currentTimeMillis();
	          System.out.print("Weights calculation...");
	          this.calculateWeight();
	          System.out.println(((System.currentTimeMillis()-executionTime)/1000) +"s ("+(System.currentTimeMillis()-executionTime)+"m)");
	          
	          //Ajout en base DOCUMENT
	          System.out.print("Adding documents...");
	          executionTime = System.currentTimeMillis();
	          handlerJDBC.addDocuments(listDocumentXMLToAdd);
	          System.out.println(((System.currentTimeMillis()-executionTime)/1000) +"s");
	          
	          //Ajout en base NODE
	          System.out.print("Adding nodes...");
	          executionTime = System.currentTimeMillis();
	          handlerJDBC.addNodes(listNodeToAdd);
	          System.out.println(((System.currentTimeMillis()-executionTime)/1000) +"s");
	          
	          //Ajout en base TERM
	          System.out.print("Adding terms...");
	          executionTime = System.currentTimeMillis();
	          handlerJDBC.addTerms(listTermToAdd);
	          System.out.println(((System.currentTimeMillis()-executionTime)/1000) +"s");
	          
	          //Ajout en base TERMNODE
	          System.out.print("Adding termNodes...");
	          executionTime = System.currentTimeMillis();
	          handlerJDBC.addTermsNodes(listTermNodeToAdd);
	          System.out.println(((System.currentTimeMillis()-executionTime)/1000) +"s");
	      
	  
	      }catch(ParserConfigurationException pce){
	          System.out.println("Erreur de configuration du parseur DOM");
	          System.out.println("lors de l'appel à fabrique.newDocumentBuilder();");
	      }catch(SAXException se){
	          System.out.println("Erreur lors du parsing du document");
	          System.out.println("lors de l'appel à construteur.parse(xml)");
	      }catch(IOException ioe){
	          System.out.println("Erreur d'entrée/sortie");
	          System.out.println("lors de l'appel à construteur.parse(xml)");
	      } catch (SQLException e2) {
			e2.printStackTrace();
		}
	}
		
	/** Initialisation de la liste des mots vides **/
	public ArrayList<String> InitlisteMotsVides () {
	      
	      ArrayList<String> listeMotsVides = new ArrayList<String>();
	      String fichier ="tools/MotsBruit.txt";
	      //lecture du fichier texte    
	      try{
	          InputStream ips=new FileInputStream(fichier); 
	          InputStreamReader ipsr=new InputStreamReader(ips);
	          BufferedReader br=new BufferedReader(ipsr);
	          String ligne;
	          while ((ligne=br.readLine())!=null){
	              listeMotsVides.add(ligne);
	          }
	          br.close(); 
	      }        
	      catch (Exception e){
	          System.out.println(e.toString());
	      }
	      
	      return listeMotsVides;
	  }
	  
		/** Récupération du chemin à partir d'un node **/
	  public static String getPathNode(Node currentNode) {
	  	
	  	ArrayList<String> tabTag = new ArrayList<String>();
	  	int cpt = 1;
	  	
	  	Node parentNode = currentNode.getParentNode();
	  	
	  	while (parentNode != null) {
	  		
	      	Node previousNode = getPreviousSiblingElement(currentNode);
	      	if (previousNode == null) {
	      		tabTag.add("/" + currentNode.getNodeName() + "[" + cpt + "]");
	      		
	      	} else {
			    	while (previousNode != null) {
			    		if (currentNode.getNodeName() == previousNode.getNodeName()) {
			    			cpt++;
			    		}
			    		previousNode = getPreviousSiblingElement(previousNode);
			    	}
			    	tabTag.add("/" + currentNode.getNodeName() + "[" + cpt + "]");
		    	}
	      	cpt = 1;
		    	currentNode = parentNode;
		    	parentNode = currentNode.getParentNode();
		    	
	  	}
	  	
	  	// On reconstruit la chaine dans le sens inverse
	  	String path="";
	  	Collections.reverse(tabTag);
			for ( String tag : tabTag) {
				path += tag;
			}
	  	return path;
	  }
	  
	  // Récupération du node précédent
	  private static Element getPreviousSiblingElement(Node node) {
	      Node prevSibling = node.getPreviousSibling();
	      while (prevSibling != null) {
	          if (prevSibling.getNodeType() == Node.ELEMENT_NODE) {
	              return (Element) prevSibling;
	          }
	          prevSibling = prevSibling.getPreviousSibling();
	      }
	      return null;  
	  }
	  
	
	// Getter and  setter
	public String getFilePath() {
		return filePath;
	}

	public void setFilePath(String filePath) {
		this.filePath = filePath;
	}
	
	
	// Poids = tf * ief
	public void calculateWeight(){
		
		for(TermNode tn : this.listTermNodeToAdd.values()){
			
			
			NodeXML nodexml = listNodeToAdd.get(tn.getIdNode());
			int numberNodeWithWord = listNumberOfNodeToTerm.get(tn.getIdTerm());
			double weight = WeightService.getWeight(tn.getFrequency(), nodexml.getSize(), this.listNodeToAdd.size(), numberNodeWithWord);
			
			tn.setWeight(weight);
		}
		
		
	}
	
	public boolean isAlphaNumeric(String s){
	    String pattern= "^[a-zA-Z0-9]*$";
	        if(s.matches(pattern)){
	            return true;
	        }
	        return false;   
	}
	
}
